﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Net;
using System.Runtime.InteropServices;

namespace InfiniTec.Net
{
	public static class Dns
	{
		public static IPHostEntry GetHostEntry(string name)
		{
			IPHostEntry result = new IPHostEntry();

			result.HostName = name;
			List<IPAddress> addresses = new List<IPAddress>();
			List<string> aliases = new List<string>();

			foreach (DnsEntry entry in Resolve(name, QueryType.A))
			{
				AddressEntry addressEntry = entry as AddressEntry;
				HostEntry hostEntry = entry as HostEntry;
				if (addressEntry != null)
				{
					addresses.Add(addressEntry.IPAddress);
				}
				else if (hostEntry != null)
				{
					aliases.Add(hostEntry.Host);
				}
			}
			result.AddressList = addresses.ToArray();
			result.Aliases = aliases.ToArray();
			return result;
		}

		internal static IEnumerable<DnsEntry> Resolve(string domain, QueryType queryType)
		{
			DnsQueryResultSafeHandle resultList;
			IntPtr reserved;

			int queryResult = SafeNativeMethods.DnsQuery(domain, queryType, QueryOptions.STANDARD, IntPtr.Zero, out resultList, out reserved);
			if (queryResult != 0)
			{
				throw new Win32Exception(queryResult);
			}

			using (resultList)
			{
				IntPtr recordHandle = resultList.DangerousGetHandle();

				while (recordHandle != IntPtr.Zero)
				{
					DNS_RECORD record = (DNS_RECORD)Marshal.PtrToStructure(recordHandle, typeof(DNS_RECORD));
					IntPtr dataHandle = (IntPtr) (recordHandle.ToInt64() + Marshal.SizeOf(typeof (DNS_RECORD)));
					recordHandle = record.pNext;
			
					switch (record.wType)
					{
							
						case QueryType.A:
							yield return new AddressEntry(MarshalData<DNS_A_DATA>(dataHandle));
							break;
						case QueryType.MX:
							case QueryType.AFSDB:
							case QueryType.RT:
							yield return new MxEntry(MarshalData<DNS_MX_DATA>(dataHandle));
							break;
						case QueryType.PTR:
						case QueryType.NS:
							case QueryType.CNAME:
                            case QueryType.MB:
							case QueryType.MD:
							case QueryType.MF:
							case QueryType.MG:
                            case QueryType.MR:
							yield return new HostEntry(MarshalData<DNS_PTR_DATA>(dataHandle));
							break;
						case QueryType.SRV:
							yield return new ServerEntry(MarshalData<DNS_SRV_DATA>(dataHandle));
							break;
						default:
							//throw new NotImplementedException(string.Format("The record type {0} is not yet supported.", record.wType));
							break;
					}
				}
			}
		}

		private static T MarshalData<T>(IntPtr handle)
			where T: struct
		{
			return (T) Marshal.PtrToStructure(handle, typeof(T));
		}
	}
}